package cn.com.itg.main;

import cn.com.itg.pojo.domain.vo.AccessTokenResponseVO;
import cn.com.itg.pojo.domain.vo.response.BaseResponse;
import cn.com.itg.util.SignHelper;
import com.alibaba.fastjson.JSON;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.BasePathMatchInterceptor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class ItgPaperTokenInterceptor extends BasePathMatchInterceptor implements Serializable {

    public static final String ACCESS_TOKEN_URL
            = "https://yonbip.diwork.com/iuap-api-auth/open-auth/selfAppAuth/getAccessToken";

    public static final String ACCESS_TOKEN_CACHE_KEY = "access_token";

    private final Cache<String, String> loadingCache;

    public ItgPaperTokenInterceptor(Cache<String, String> loadingCache) {
        this.loadingCache = loadingCache;
    }

    @Override
    protected Response doIntercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Map<String, String> params = new HashMap<>();
        // 除签名外的其他参数
        params.put("access_token", this.getAccessToken(true));
        //获取老的url
        String oldUrl = originalRequest.url().toString();
        String urlSuffix;
        if (oldUrl.contains("?")) {
            urlSuffix = this.buildGetParams2(params);
        } else {
            urlSuffix = this.buildGetParams1(params);
        }
        Request build = originalRequest.newBuilder().url(oldUrl + urlSuffix).build();
        log.info("url is {}", oldUrl + urlSuffix);
        Response proceed = chain.proceed(build);
        int code = proceed.code();
        log.info("code is {}", code);

        MediaType mediaType = proceed.body().contentType();
        String body = proceed.body().string();
        log.info("body is {}", body);
        BaseResponse baseResponse = JSON.parseObject(body, BaseResponse.class);
        // 请求如果是310036，token认证失败就重新获取token再请求一次
        if (StringUtils.equals("310036", baseResponse.getCode())) {
            params.put("access_token", this.getAccessToken(false));
            return chain.proceed(build);
        }
        return proceed.newBuilder()
                .body(ResponseBody.create(mediaType, body))
                .build();
    }


    /**
     * 原生http请求
     */
    private synchronized String getAccessToken(Boolean tokenStatus) throws IOException {
        // token 验证失败的时候接口获取token
        if (!tokenStatus) {
            return this.getTokenResponse();
        }
        // 获取缓存中的token
        String accessToken = loadingCache.getIfPresent(ACCESS_TOKEN_CACHE_KEY);
        if (StringUtils.isNotBlank(accessToken)) {
            return accessToken;
        }
        // 缓存中获取不到通过接口获取
        return this.getTokenResponse();
    }


    private String getTokenResponse() throws IOException {
        //1 构造Request
        OkHttpClient okHttpClient = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        Map<String, String> params = new HashMap<>();
        // 除签名外的其他参数
        params.put("appKey", "5cb9eb6288244963b8f92491e3f9dd38");
        params.put("timestamp", String.valueOf(System.currentTimeMillis()));
        // 计算签名
        String signature = null;
        try {
            signature = SignHelper.sign(params, "f6c14d9fa4884de3b5262fde7001e552");
        } catch (Exception e) {
            log.info("获取token签名失败：【{}】", e.getMessage());
            throw new RuntimeException(e);
        }
        params.put("signature", signature);
        String param = this.buildGetParams1(params);
        Request request = builder.get().url(ACCESS_TOKEN_URL + param).build();
        //2 将Request封装为Call
        Call call = okHttpClient.newCall(request);
        //3 执行Call，得到response
        Response tokenResponse = call.execute();
        if (tokenResponse.code() != 200) {
            throw new RuntimeException("请求accessToken失败-网络请求返回非200");
        }
        assert tokenResponse.body() != null;
        String tokenBodyStr = tokenResponse.body().string();
        AccessTokenResponseVO accessTokenResponseVO = JSON.parseObject(tokenBodyStr, AccessTokenResponseVO.class);
        if (accessTokenResponseVO == null
                || !StringUtils.equals(accessTokenResponseVO.getCode(), "00000")) {
            throw new RuntimeException("请求accessToken失败-业务code非00000");
        }
        String accessToken = accessTokenResponseVO.getData().getAccessToken();
        // 获取到token放入缓存
        loadingCache.put(ACCESS_TOKEN_CACHE_KEY, accessToken);
        return accessToken;
    }

    private String buildGetParams1(Map<String, String> params) {
        StringBuilder param = new StringBuilder("?");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            param.append(entry.getKey());
            param.append("=");
            param.append(entry.getValue());
            param.append("&");
        }
        param.deleteCharAt(param.length() - 1);
        return param.toString();
    }

    private String buildGetParams2(Map<String, String> params) {
        StringBuilder param = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            param.append("&");
            param.append(entry.getKey());
            param.append("=");
            param.append(entry.getValue());
        }
        return param.toString();
    }
}
